package controlPlane;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
import multidomain.Domain;
import multidomain.TED;
import events.*;
import tracing.Tracer;
import util.Dijkstra;
import util.WeightedGraph;
import wdmsim.*;

/**
 * crankback = true;
 * wavelengthConversion = false;
 * multiHop = false;
 * grooming = true || false; // to false, put traffic.bw = link.bw
 * multipath = false;
 * wavelengthDecision = source;
 * 
 * @author Alisson
 *
 */
public class PCE extends Node implements MultidomainControlPlane {

	/** */
	Tracer trace = Tracer.getTracerObject();
	/** */
	EventScheduler scheduler = EventScheduler.getInstance();
	/** */
	PhysicalTopology pt = PhysicalTopology.getInstance();
	/** Flows that have been accepted or that are waiting for a decision */
	private Map<Long, Flow> mappedFlows;
	/** Flows that have been accepted into the network */
    private Map<Flow, Path> activeFlows;
    /** */
	private TED ted;
	
	/**
	 * 
	 * @param id
	 * @throws Exception
	 */
	public PCE(int id) throws Exception {
		this.id = id;
		this.ted = new TED(id);
		//generateUpdateEvent();
		mappedFlows = new HashMap<Long, Flow>();
        activeFlows = new HashMap<Flow, Path>();
	}
	
	/**
	 * 
	 * @return
	 */
	public TED getTed() {
		return ted;
	}
	
	/**
	 * 
	 * @param ted
	 */
	public void setTed(TED ted) {
		this.ted = ted;
	}
	
	/**
	 * 
	 * @return
	 */
	public Map<Flow, Path> getActiveFlows() {
		return activeFlows;
	}

	/**
	 * 
	 * @param activeFlows
	 */
	public void setActiveFlows(HashMap<Flow, Path> activeFlows) {
		this.activeFlows = activeFlows;
	}
	
	/**
	 * 
	 */
	private void generateUpdateEvent() {
		UpdateEvent updateEvent = new UpdateEvent(this);
		scheduler.schedule(updateEvent, (long)(Math.random()*Constants.UPDATE_TIME));
	}
	
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateAbortEvent()
	 */
	@Override
	public void generateAbortEvent(Event event) throws Exception {
		if(event instanceof CrankbackEvent) {
			// Para todo PCE verifica se ele tem um mappedFlow igual a flow e desaloca recursos
			Domain[] domains = pt.getDomainVector();
			boolean bool = true;
			for(int i=0; i<pt.getNumDomains(); i++) {
				if(domains[i].getPce().hasMappedFlow(((CrankbackEvent)event).getFlow())) {
					bool = domains[i].getPce().crankbackReleaseResources(((CrankbackEvent)event).getFlow());
					if(!bool)
						break;
				}
			}
			if(bool) {
				trace.reportBlockedCall(event);
				
			} else {
				throw new Exception();
			}
			
		} else if(event instanceof RouteRequestEvent) {
			// Para todo PCE verifica se ele tem um mappedFlow igual a flow e desaloca recursos
			Domain[] domains = pt.getDomainVector();
			boolean bool = true;
			for(int i=0; i<pt.getNumDomains(); i++) {
				if(domains[i].getPce().hasMappedFlow(((RouteRequestEvent)event).getFlow())) {
					bool = domains[i].getPce().crankbackReleaseResources(((RouteRequestEvent)event).getFlow());
					if(!bool)
						break;
				}
			}
			if(bool) {
				
				
			} else {
				throw new Exception();
			}
		}
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateCrankbackEvent()
	 */
	@Override
	public void generateCrankbackEvent(RouteRequestEvent event) {
		CrankbackEvent crankbackEvent = new CrankbackEvent(event);
		scheduler.schedule(crankbackEvent, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME);
	}

	@Override
	public void generateDestroyLightpathEvent(Flow flow) throws Exception {
		DestroyLightpathEvent event = new DestroyLightpathEvent(flow);
		scheduler.schedule(event, Simulator.CURRENT_TIME+flow.getDuration());		
	}
	
	public void generateDestroyLightpathEvent(Flow flow, Node generator, Handler handler) throws Exception {
		DestroyLightpathEvent event = new DestroyLightpathEvent(flow, generator, handler);
		scheduler.schedule(event, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME);		
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateRouteRequestEvent()
	 */
	@Override
	public void generateRouteRequestEvent(Event event) {
		// TODO Auto-generated method stub
		
	}
	
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateRouteReply(events.Event)
	 */
	@Override
	public void generateRouteReply(Event event) {
		// TODO Auto-generated method stub
		
	}
	
	/* (non-Javadoc)
	 * @see controlPlane.Handler#handle(wdmsim.Event)
	 */
	@Override
	public void handle(Event event) throws Exception {
		if(event instanceof RouteRequestEvent) {
			this.handleRouteRequestEvent((RouteRequestEvent)event);			
			
		} else if(event instanceof CrankbackEvent) {
			this.handleCrankbackEvent((CrankbackEvent)event);
			
		} else if(event instanceof DestroyLightpathEvent) {
			this.handleDestroyLightpathEvent((DestroyLightpathEvent)event);
			
		} else if(event instanceof UpdateEvent) {
			this.handleUpdateEvent((UpdateEvent)event);
		
		} else {
			
		}
	}
	
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#routeReply()
	 */
	@Override
	public void handleRouteReply(Event event) {
		// TODO Auto-generated method stub
		
	}
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handleAbortEvent()
	 */
	@Override
	public void handleAbortEvent(Event event) {
		// TODO Auto-generated method stub
		
	}
	
	/**
	 * 
	 * @param event
	 * @throws Exception
	 */
	public void handleUpdateEvent(UpdateEvent event) throws Exception {
		if(this.id==((PCE)event.getHandler()).getId()) {
			for(int d=0; d<pt.getNumDomains(); d++) {
				if(pt.areNeighbourDomains(this.id, d)) {
					this.getTed().updateF(pt.getDomain(d).getPce());
				}
			}
			if((Simulator.CURRENT_TIME+Constants.UPDATE_TIME) < Simulator.SIMULATION_TOTAL_TIME)
				scheduler.schedule(new UpdateEvent(event), (long)Simulator.CURRENT_TIME+Constants.UPDATE_TIME);
		
		} else {
			throw new Exception();
		}
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handleCrankbackEvent()
	 */
	@Override
	public void handleCrankbackEvent(CrankbackEvent event) throws Exception {
		// se esse pce for o handler de event
		if(this.id==((PCE)event.getHandler()).getId()) {
			Vector<Domain> domainPath = event.getFlow().getDomainPath();
			domainPath.removeElement(pt.getDomain(event.getGenerator().getId()));
			
			if(crankbackReleaseResources(event.getFlow())) {
				mappedFlows.remove(event.getFlow().getId());
			} else {
				throw new Exception();
			}
			// atualizar a ted desse pce com as informacoes do pce gerador
			ted.updateF();			
			((PCE)event.getGenerator()).getTed().updateF(this);
			
			// se o numero de crankbacks nao estourou
			if(event.getFlow().getTries()<Constants.CONECTION_TRIES) {
				//if(this.id==pt.getDomainFromNode(event.getFlow().getSrc()).getID()) {
					event.getFlow().increaseTries();
				//}
				
				if(domainPath.size()>1) {
					// gerador eh
					event.setGenerator(domainPath.get(domainPath.size()-2).getPce());
					
				} else {
					event.setGenerator(this);
				}
				
				// if ted tem outro caminho
				if(ted.hasRegister(pt.getDomainFromNode(event.getFlow().getDst()))) {
					domainPath.removeElement(pt.getDomain(this.getId()));
					handleRouteRequestEvent(new RouteRequestEvent(event));
					
				// else if esse pce for o gerador do fluxo
				} else if(pt.getDomainFromNode(event.getFlow().getSrc()).getID()==this.id) {
					trace.reportBlockedCall(event);
					
				} else {
					generateCrankbackEvent(new RouteRequestEvent(event));
				}
				
			} else {
				generateAbortEvent(event);
			}
			
		} else {
			throw new Exception();
		}
	}

	/**
	 * 
	 * @param event
	 * @throws Exception
	 */
	public void handleDestroyLightpathEvent(DestroyLightpathEvent event) throws Exception {
		if(this.id==((PCE)event.getHandler()).getId()) {
			if(!destroyLightpath(event.getFlow())) {
				throw new Exception();
			}
		}
		ted.updateF();
	}
		
	
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handleRouteRequestEvent()
	 */
	@Override
	public void handleRouteRequestEvent(RouteRequestEvent event) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		Flow flow = ((RouteRequestEvent) event).getFlow();
		flow.addDomain(pt.getDomain(this.id));
		int[] multidomainPath = null;
		int[] intradomainPath = null;
		OXC outBorderNode = null, inBorderNode = null;
		
		// caso em que o fluxo eh intradominio
		if(this.id==pt.getDomainFromNode(flow.getSrc()).getID() && this.id==pt.getDomainFromNode(flow.getDst()).getID()) {
			Flow f = alreadyCreatedLightpath(flow);
			if(f!=null) {
				addFlowToLightpath(f.getPath(), flow);
				activeFlows.put(flow, f.getPath());
				
			} else {
				Lightpath lp = createIntradomainLightpath(flow.getSrc(), flow.getDst(), flow.getBw());
				if(lp.getNumLinks()>0) {
					flow.addLightpath(lp);
					activeFlows.put(flow, flow.getPath());
					trace.reportLightpathCreated(event);
					generateDestroyLightpathEvent(flow);
					ted.updateF();
				} else {
					trace.reportBlockedCall(event);
				}
			}
		
		// caso seja o PCE fonte
		} else if(this.id==pt.getDomainFromNode(flow.getSrc()).getID()) {
			int tries = 0;
			if(flow.getRequestArrivalTime()==0)
				flow.setRequestArrivalTime(Simulator.CURRENT_TIME);
			boolean bool = false;
			int[] wavelength = null;
			Random random = new Random();
			int w;
			int t = -1;
			Flow f = null;
			boolean bool2 = false;
			do {
				t++;
				f = null;
				f = alreadyCreatedLightpath(flow, t);
				if(f!=null) {
					bool2 = addFlowToLightpath(f.getPath(), flow); 
					if(bool2) {
						registerFlowToDomains(flow.getDomainPath(), flow);
						trace.reportFlowInserting(event);
						generateDestroyLightpathEvent(flow);
						ted.updateF();
						
					} else {
						flow.setDomainPath(new Vector<Domain>());
						flow.addDomain(pt.getDomain(this.id));
						flow.setPath(new Path());
					}
				}
			}while(bool2==false && f!=null);
			
			if(bool2==false) {
				do {
				
					// verifica se tem caminho para dominio destino
					TED.TableRow tableRow = ted.getBestMultidomainRowF(pt.getDomainFromNode(flow.getDst()).getID(), flow.getBw());
					if(tableRow==null) multidomainPath=null; else multidomainPath=tableRow.getPath();
					
					if(multidomainPath != null) {
						outBorderNode = pt.getOutBorderNode(this.id, multidomainPath[1]);
						wavelength = tableRow.getFreeWavelengths();
					
						if(wavelength.length>0) {
							
							w = random.nextInt(wavelength.length);
							// se src for o proprio outBorderNode
							if(flow.getSrc()==outBorderNode.getId()) {
								
								bool = reserveInterdomainLink(this, (Handler)pt.getDomain(multidomainPath[1]).getPce(), flow, wavelength[w]);
								if(bool) {
									RouteRequestEvent fowardEvent = new RouteRequestEvent(flow, this);
									fowardEvent.setHandler((Handler)pt.getDomain(multidomainPath[1]).getPce());
									scheduler.schedule(fowardEvent, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME);
									mappedFlows.put(flow.getId(), flow);
									
								}
								
							} else {
								// verifica se tem caminho de src para outBorderNode
								intradomainPath = pt.getIntradomainPath(this.id, flow.getSrc(), outBorderNode.getId(), flow.getSrc(), flow.getBw(), wavelength[w]);
								
								if(intradomainPath!=null && outBorderNode!=null) {
									Lightpath lp = createIntradomainLightpath(intradomainPath, flow.getSrc(), flow.getSrc(), flow.getBw(), wavelength[w]);
									bool = false;
									if(lp.getNumLinks()>0) {
										flow.addLightpath(lp);
										bool = reserveInterdomainLink(this, (Handler)pt.getDomain(multidomainPath[1]).getPce(), flow, lp.getWavelength());
									}
									// se o lightpath e o interdomainLink foram reservados com sucesso
									if(bool) {
										RouteRequestEvent fowardEvent = new RouteRequestEvent(flow, this);
										fowardEvent.setHandler((Handler)pt.getDomain(multidomainPath[1]).getPce());
										scheduler.schedule(fowardEvent, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME); // podemos adicionar o delay do enlace interdominio
										mappedFlows.put(flow.getId(), flow);
										
									} else if(lp.getNumLinks()>0){
										if(!releaseIntradomainLightpath(flow, lp)) throw new Exception();
									}
								}
							}
						}
						
					}	
					tries++;
				} while(multidomainPath!=null && !bool && (tries<=wavelength.length || tries<pt.getNumBorderNodes(this.id)));
				ted.updateF();
				
				if(!bool) {
					trace.reportBlockedCall(event);
				}
			}
		
		// caso seja o PCE destino
		} else if(this.id==pt.getDomainFromNode(flow.getDst()).getID()) { 
			
			inBorderNode = pt.getInBorderNode(event.getGenerator().getId(), this.id);
			// se dst for o proprio inBorderNode
			if(flow.getDst()==inBorderNode.getId()) {
				mappedFlows.put(flow.getId(), flow);
				this.createMultidomainLightpath(flow);
				Simulator.CURRENT_TIME += Constants.DOMAIN_PROCESSING_TIME;
				trace.reportLightpathCreated(event);
				generateDestroyLightpathEvent(flow);
				
			} else {
				intradomainPath = pt.getIntradomainPath(this.id, inBorderNode.getId(), flow.getDst(), flow.getSrc(), flow.getBw(), flow.getPath().getLightpaths().lastElement().getWavelength());
				
				if(intradomainPath!=null && inBorderNode!=null) {
					Lightpath lp = createIntradomainLightpath(intradomainPath, flow.getSrc(), flow.getSrc(), flow.getBw(), flow.getPath().getLightpaths().lastElement().getWavelength());
					if(lp.getNumLinks()>0) {
						flow.addLightpath(lp);
						mappedFlows.put(flow.getId(), flow);
						this.createMultidomainLightpath(flow);
						Simulator.CURRENT_TIME += Constants.DOMAIN_PROCESSING_TIME;
						trace.reportLightpathCreated(event);
						generateDestroyLightpathEvent(flow);
						System.out.println(flow.getId());
						
					} else {
						generateAbortEvent(event);
						trace.reportBlockedCall(event);
					}
					
				} else {
					generateAbortEvent(event);
					trace.reportBlockedCall(event);
				}
			}
			//ted.updateF();
		// caso seja um PCE intermediario
		} else {			
			boolean bool = false;
			int tries = 0;
			Lightpath lp = null;
			Vector<Integer> bedDomains = new Vector<Integer>();
			do {
				int borderNode = pt.getInBorderNode(event.getGenerator().getId(), this.id).getId();
				// verifica se tem caminho para dominio destino
				multidomainPath = ted.getBestMultidomainPathF(bedDomains, borderNode, pt.getDomainFromNode(flow.getDst()).getID(), flow.getBw(), flow.getPath().getLightpaths().lastElement().getWavelength());
									
				if(multidomainPath != null && !makesLoop(flow.getDomainPath(), multidomainPath)) {
					// verifica se tem caminho de inBorderNode para outBorderNode
					outBorderNode = pt.getOutBorderNode(this.id, multidomainPath[1]);
					inBorderNode = pt.getInBorderNode(event.getGenerator().getId(), this.id);
					intradomainPath = pt.getIntradomainPath(this.id, inBorderNode.getId(), outBorderNode.getId(), flow.getSrc(), flow.getBw(), flow.getPath().getLightpaths().lastElement().getWavelength());
					// caso o outBorderNode seja igual ao inBorderNode
					if(intradomainPath!=null && intradomainPath.length==1) {
						bool = reserveInterdomainLink(this, (Handler)pt.getDomain(multidomainPath[1]).getPce(), flow, flow.getPath().getLightpaths().lastElement().getWavelength());
					}
					else if(intradomainPath!=null && outBorderNode!=null && inBorderNode!=null) {
						lp = createIntradomainLightpath(intradomainPath, flow.getSrc(), flow.getSrc(), flow.getBw(), flow.getPath().getLightpaths().lastElement().getWavelength());
						if(lp.getNumLinks()>0) {
							flow.addLightpath(lp);
							bool = reserveInterdomainLink(this, (Handler)pt.getDomain(multidomainPath[1]).getPce(), flow, lp.getWavelength());
							
						}
					}
					if(bool) {													
						RouteRequestEvent fowardEvent = new RouteRequestEvent(flow, this);
						fowardEvent.setHandler((Handler)pt.getDomain(multidomainPath[1]).getPce());
						scheduler.schedule(fowardEvent, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME); // podemos adicionar o delay do enlace interdominio
						mappedFlows.put(flow.getId(), flow);
						
					} else if(lp!=null && lp.getNumLinks()>0){
						Vector<Lightpath> lpVector = flow.getPath().getLightpaths();
						if(!releaseIntradomainLightpath(flow, lpVector.lastElement())) throw new Exception();
					}
					
				} else if(multidomainPath!=null && makesLoop(flow.getDomainPath(), multidomainPath)) {
					bedDomains.add(multidomainPath[1]);
				}
				tries++;
			} while(multidomainPath!=null && !bool && tries<pt.getNumBorderNodes(this.id));
			ted.updateF();
			
			if(!bool) {
				generateCrankbackEvent(event);
				trace.reportCrankBackEvent(event);
			}
		}
	}

	/**
	 * @param domainPath
	 * @param flow
	 */
	private void registerFlowToDomains(Vector<Domain> domainPath, Flow flow) {
		for(int i=0; i<domainPath.size(); i++) {
			Domain domain = domainPath.get(i);
			PCE pce = domain.getPce();
			pce.activeFlows.put(flow, flow.getPath());
		}
		
	}

	/**
	 * @param path
	 * @param flow
	 */
	private boolean addFlowToLightpath(Path path, Flow flow) {
		Vector<Lightpath> lpVector = path.getLightpaths();
		boolean bool = false;
		
		for(int i=0; i<lpVector.size(); i++) {
			Lightpath lp = lpVector.get(i);
			int j;
			for(j=0; j<lp.getNumLinks(); j++) {
				WDMLink link = lp.getLink(j);
				bool = link.addFlow(lp.getWavelength(), flow.getBw(), flow.getSrc());
				if(!bool)
					break;
			}
			if(!bool) {
				for(int k=0; k<j; k++) {
					WDMLink link = lp.getLink(k);
					link.release(lp.getWavelength(), flow);
				}
				break;
			}
		}
		return bool;
	}

	/**
	 * @param src
	 * @param dst
	 * @return
	 * @throws CloneNotSupportedException 
	 */
	private Flow alreadyCreatedLightpath(Flow flow) throws CloneNotSupportedException {
		Iterator<Flow> it = activeFlows.keySet().iterator();
		for(int i=0; i<activeFlows.size(); i++) {			
			Flow f = it.next();
			if(f.getSrc()==flow.getSrc() && f.getDst()==flow.getDst()) {
				flow.setDomainPath((Vector<Domain>)f.getDomainPath().clone());
				flow.setPath((Path)f.getPath().clone());
				return f;
			}
		}
		
		return null;
	}
	
	private Flow alreadyCreatedLightpath(Flow flow, int trie) throws CloneNotSupportedException {
		Iterator<Flow> it = activeFlows.keySet().iterator();
		int tries = -1;
		
		for(int i=0; i<activeFlows.size(); i++) {			
			Flow f = it.next();
			if(f.getSrc()==flow.getSrc() && f.getDst()==flow.getDst()) {
				tries++;
				if(tries==trie) {
					flow.setDomainPath((Vector<Domain>)f.getDomainPath().clone());
					flow.setPath((Path)f.getPath().clone());
					return f;
				}
				
			}
		}
		
		return null;
	}

	/**
	 * 
	 * @param domainPath
	 * @param multidomainPath
	 * @return
	 */
	private boolean makesLoop(Vector<Domain> domainPath, int[] multidomainPath) {
		for(int i=0; i<domainPath.size(); i++) {
			for(int j=1; j<multidomainPath.length; j++) {
				if(domainPath.get(i).getID()==multidomainPath[j]) {
					return true;
				}
			}
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#createMultidomainLightpath()
	 */
	@Override
	public void createMultidomainLightpath(Flow flow) {
		// Para todo PCE verifica se ele tem um mappedFlow igual a flow e torna-os activeFlow
		Domain[] domains = pt.getDomainVector();
		for(int i=0; i<pt.getNumDomains(); i++) {
			if(domains[i].getPce().hasMappedFlow(flow)){
				domains[i].acceptFlow(flow);
			}
		}
	}
	
	/**
	 * 
	 * @param generator
	 * @param handler
	 * @param flow
	 * @param wavelength
	 * @return
	 * @throws Exception
	 */
	private boolean reserveInterdomainLink(Node generator, Handler handler, Flow flow, int wavelength) throws Exception {
		Vector<WDMLink> interdomainLinkVector = pt.getInterdomainLinkVector();
		boolean r = false;
		Lightpath lp = new Lightpath();
		for(int i=0; i<interdomainLinkVector.size(); i++) {
			WDMLink link = interdomainLinkVector.get(i);
			// se o link tiver src igual a generator e dst igual a handler
			if(pt.getDomainFromNode(link.getSrcId()).getID()==generator.getId() &&
					pt.getDomainFromNode(link.getDstId()).getID()==((PCE)handler).getId()) {
				// reserva recursos caso haja suficiente em wavelength
				r = link.reserveResource(wavelength, flow.getBw(), flow.getSrc());
				if(r) {
					lp.setWavelength(wavelength);
					lp.addLink(link);
					flow.addLightpath(lp);
				}
				break;
				
			}
		}
		return r;
	}
	
	/**
	 * @throws Exception 
	 * reserva recursos em todos os enlaces entre srcNode e dstNode.
	 * retorna boolean se obteve ou nao sucesso
	 */
	private Lightpath createIntradomainLightpath(int path[], int src, int flowSrc, int resources, int wavelength) throws Exception {
		Lightpath lightpath = new Lightpath();
		WDMLink link;
		boolean bool = false;
		int w = wavelength; int i;
		// verifica se em todos os enlaces de path ha bandwidth disponivel em wavelength
		//while(!bool) {
			for(i=0; i<path.length-1; i++) {
				link = pt.getLink(path[i], path[i+1]);
				if(link.getWavelengthAvailableBandwidth(w)>=resources && link.getOwn(wavelength)==-1) {
					bool = true;
					
				} else {
					bool = false;
					break;
				}
			}
			
		//}
		if(bool) {
			lightpath.setWavelength(w);
			for(int j=0; j<path.length-1; j++) {
				link = pt.getLink(path[j], path[j+1]);				
				if(link.reserveResource(w, resources, flowSrc)) {
					lightpath.addLink(link);
					
				} else {
					throw new Exception();
				}
				
			}
		}
		return lightpath;
	}
	
	/**
	 * 
	 * @param srcNode
	 * @param dstNode
	 * @param resources
	 * @return
	 * @throws Exception
	 */
	private Lightpath createIntradomainLightpath(int srcNode, int dstNode, int resources) throws Exception {
		WeightedGraph graph = pt.getIntradomainWeightedGraph(this.id);
		Lightpath lightpath = new Lightpath();
		int path[];
		boolean bool = false;
		
		do {
			path = Dijkstra.getShortestPath(graph, srcNode, dstNode);			
			WDMLink link;			
			int w = -1; int i=0;
			
			if(path!=null && path.length>0) {
				// encontra wavelength que tenha recurso disponivel em todos os enlaces de path
				while(!bool && w<pt.getWavelengths()-1) {
					w++;
					bool = true;
					for(i=0; i<path.length-1; i++) {
						link = pt.getLink(path[i], path[i+1]);
						if(!(link.getWavelengthAvailableBandwidth(w)>=resources && link.getOwn(w)==-1)) {
							bool = false;
							break;
						}
					}
				}
				
			} else {
				break;
			}
			if(w<pt.getWavelengths() && bool) {
				lightpath.setWavelength(w);
				for(int j=0; j<path.length-1; j++) {
					link = pt.getLink(path[j], path[j+1]);				
					bool = link.reserveResource(w, resources, srcNode);
					if(bool) lightpath.addLink(link);
					else throw new Exception();
				}
				
			} else {
				// delete any edge of path
				// random variable between 1 and i+1 (Min + (int)(Math.random() * ((Max - Min) + 1)))
				int i2 = 1 + (int)(Math.random() * (((i+1) - 1) + 1));
				int i1 = i2-1;
				//graph.removeEdge(path[i], path[i+1]);
				graph.removeEdge(path[i1], path[i2]);
			}
		} while(path.length>0 && !bool);
		
		return lightpath;
	}

	/**
	 * 
	 */
	public void activeFlow(Flow flow) {
		if(mappedFlows.containsKey(flow.getId())) {
			mappedFlows.remove(flow.getId());
			activeFlows.put(flow, flow.getPath());
		}
	}

	/**
	 * 
	 * @param flow
	 * @return
	 * @throws Exception
	 */
	private boolean destroyLightpath(Flow flow) throws Exception {
		// destroy a porcao de lightpath que passar pelo dominio do pce
		if(pt.destroyLightpath(flow, this)) {
			// gera um destroy lightpath com handle o proximo pce do caminho de flow
			activeFlows.remove(flow);
			flow.removeDomain();
			if(flow.getDomainPath().size()>0)
				generateDestroyLightpathEvent(flow, this, flow.getDomainPath().get(0).getPce());
			return true;
			
		} else {
			return false;
		}
	}
	
	/**
	 * @param flow
	 */
	public boolean releaseResources(Flow flow) {
		Path path = flow.getPath();
		boolean released = false;
		if(activeFlows.containsKey(flow)) {
			released = true;
			for(int i=0; i<path.getLightpaths().size(); i++) {
				if(!path.getLightpaths().get(i).releaseLightpath(flow)) {
					released = false;
					break;
				}
			}
		}
		return released;
	}
	
	/**
	 * 
	 * @param flow
	 * @param lp
	 * @return
	 * @throws Exception
	 */
	public boolean releaseIntradomainLightpath(Flow flow, Lightpath lp) throws Exception {
		boolean bool = lp.releaseLightpath(flow, this);
		if(lp.isEmpty()) {
			flow.getPath().removeLightpath(lp);
		}
		return bool;
	}
	
	/**
	 * 
	 * @param flow
	 * @return
	 * @throws Exception
	 */
	public boolean crankbackReleaseResources(Flow flow) throws Exception {
		Path path = flow.getPath();
		boolean released = false;
		if(mappedFlows.containsKey(flow.getId())) {
			released = true;
			for(int i=0; i<path.getLightpaths().size(); i++) {
				if(!path.getLightpaths().get(i).releaseLightpath(flow, this)) {
					released = false;
					break;					
				}
				if(path.getLightpaths().get(i).isEmpty()) {
					path.removeLightpath(path.getLightpaths().get(i));
				}
			}
			
		}
		// limpa os lightpaths que nao tem nenhum link, por terem sido removidos
		if(released) {
			int i = 0;
			while(i<path.getLightpaths().size()) {
				if(path.getLightpaths().get(i).isEmpty()) {
					path.removeLightpath(path.getLightpaths().get(i));
					
				} else {
					i++;
				}
			}
		}
		
		return released;
	}

	/**
	 * @return
	 */
	private boolean hasMappedFlow(Flow flow) {
		if(mappedFlows.containsKey(flow.getId())) {
			return true;
			
		} else {
			return false;
		}
	}
}
	 